home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / hypercar / xfcn / spttool.cpt / Support Tools eXternals 1.2.5 / card_3676.txt < prev    next >
Text File  |  1990-11-13  |  25KB  |  683 lines

  1. -- card: 3676 from stack: in.5
  2. -- bmap block id: 4534
  3. -- flags: 0000
  4. -- background id: 3858
  5. -- name: VolumePath
  6. ----- HyperTalk script -----
  7. on hideObjects
  8.   hide cd btn "Try It!"
  9. end hideObjects
  10.  
  11. on showObjects
  12.   show cd btn "Try It!"
  13. end showObjects
  14.  
  15.  
  16. -- part 6 (button)
  17. -- low flags: 00
  18. -- high flags: A004
  19. -- rect: left=60 top=177 right=213 bottom=191
  20. -- title width / last selected line: 0
  21. -- icon id / first selected line: 0 / 0
  22. -- text alignment: 1
  23. -- font id: 0
  24. -- text size: 12
  25. -- style flags: 8192
  26. -- line height: 16
  27. -- part name: Try it!
  28. ----- HyperTalk script -----
  29. on mouseUp
  30.   answer "You chose ΓÇ£" & VolumePath() & "ΓÇ¥"
  31. end mouseUp
  32.  
  33.  
  34.  
  35.  
  36. -- part contents for background part 20
  37. ----- text -----
  38.      VolumePath displays a modified Standard File dialog to let the user choose a volume.   It returns the full path name of the chosen volume, or empty if the CANCEL button is chosen.
  39.  
  40.       The dialog window is centered in the HyperCard window, regardless of which monitor it is on.  In addition to the standard Eject, Drive, Select, and Cancel buttons, the XFCN displays the amount of free space on a volume.
  41.  
  42.       As with all of our XCMDs and XFCNs, passing a single question mark (VolumeName("?") in this case) returns the syntax for the external.  Passing two question marks (VolumeName("??")) or a single exclamation mark (VolumeName("!")) returns the copyright information.
  43.  
  44. -- part contents for background part 38
  45. ----- text -----
  46. 50/50
  47.  
  48. -- part contents for background part 42
  49. ----- text -----
  50. { VolumeName() XFCN source listing}
  51. { This is an XFCN that brings up a custom standard file dialog to allow the user to select a volume.}
  52. { This source file is part of a stack containing all necessary source code and compiled versions of}
  53. { this XFCN as well as 2 other standard file XFCN's.  Send requests for the stack to the applelink}
  54. { addresses below.}
  55. {}
  56. { This XFCN is not to be sold commercially or included within any commercial product}
  57. { without specific authorization from the authors and Apple Computer, Inc.}
  58. {}
  59. {  Written by:  Anup Murarka    Eric Carlson    }
  60. {        ALINK:  SKEPTIC    ALINK:  cyNic  }
  61. {                  CIS:  76004,3356    }
  62. {}
  63. {        We are part of the Support Tools Development Group,  }
  64. {        Apple Computer, Inc.  }
  65. {}
  66. {        please DO NOT contack Mac DTS for support of this code!  }
  67. {}
  68. {        please DO contact the authors for support of this code!  }
  69. {}
  70. {        Send comments, bug reports, requests to any of the above  }
  71. {        E-mail addresses or to:}
  72. {}
  73. {              (one of us)          }
  74. {              Apple Computer, Inc.    }
  75. {              900 E. Hamilton, Ave.    }
  76. {              Campbell, CA   95008    }
  77. {              M/S 72-L          }
  78. {}
  79. {  Copyright:  ┬⌐ 1989, 1990 by Apple Computer, Inc., all rights reserved.  }
  80. {}
  81. { written by  : Anup Murarka                                        }
  82. { AppleLink  : Skeptic                                            }
  83. { modification history                                             }
  84. {       Date      Initials                  Comments                }
  85. {       ----      ------    ------------------------------------------------------}
  86. {    11/29/89  ec&akm    first written                              }
  87. {    8/14/90        ec      recompiled with new libraries for Modal Dialog update  bug  }
  88. {                      & A/UX correct path construction. Changed version to 1.1  }
  89. {}
  90. unit dummyUnit;
  91.  
  92. interface
  93.  
  94.   uses
  95.     HyperXCMD;
  96.  
  97.   procedure main (paramPtr: XCmdPtr);
  98.  
  99. implementation
  100.  
  101.   procedure VolumeName (paramPtr: XCmdPtr);
  102.   FORWARD;
  103.  
  104.   procedure main (paramPtr: XCmdPtr);
  105.   begin
  106.     VolumeName(paramPtr);
  107.   end;
  108.  
  109.   const
  110.     kSFSaveDisk = $214;        { Negative of current volume refnum [WORD]  }
  111.     kApplScratch = $00000A78;
  112.     kCurDirStore = $398;        { DirID of current directory [LONG]        }
  113.     DITLSizeDiff = 30;
  114.  
  115.   type
  116.     DITLItem = record
  117.         itmHndl: handle;
  118.         itmRect: rect;
  119.         itmType: SignedByte;
  120.         itmData: SignedByte;     { This is really only the length byte.  Data follows of variable length}
  121. {    itmData is followed by the actual data.  See IM I-427}
  122.       end;
  123.     pDITLItem = ^DITLItem;
  124.     hDITLItem = ^pDITLItem;
  125.  
  126.     ItemList = record
  127.         dlgMaxIndex: integer;
  128.         DITLItems: array[0..0] of DITLItem;
  129.       end;
  130.     pItemList = ^ItemList;
  131.     hItemList = ^pItemList;
  132.  
  133.     integerPtr = ^integer;
  134.  
  135.   procedure reportToUser (paramPtr: XCmdPtr;
  136.                   msgStr: str255);
  137. {}
  138. { report something back to the user.  }
  139. { the last parameter (optional) to an external may contain }
  140.  { "noDialog" or "noDialog:GlobalName".  GlobalName is the name }
  141.  { of a HyperTalk global variable into which error messages will be }
  142.  { placed.  we've decided to use this approach to avoid confusing }
  143. { an error message with a valid result being returned from an XFCN. }
  144. {}
  145.     var
  146.       tempStr: str255;
  147.   begin
  148. {check the last param to see if the user requested that}
  149. { we suppress the error dialog }
  150.     ZeroToPas(paramPtr, paramPtr^.params[paramPtr^.paramCount]^, tempStr);
  151.     UprString(tempStr, true);
  152.     if pos('NODIALOG', tempStr) = 0 then
  153.   { no special error handling specified, throw up a dialog and return the error message }
  154.       begin
  155.         SendCardMessage(paramPtr, concat('answer "', msgStr, '"'));
  156.         paramPtr^.returnValue := PasToZero(paramPtr, msgStr);
  157.       end
  158.     else if (pos(':', tempStr) > 0) then
  159.   { requested global AND noDialog so we fill in the global and return empty }
  160.       begin
  161.         tempStr := copy(tempStr, pos(':', tempStr) + 1, length(tempStr));
  162.                             { get the name of the HC global  to fill }
  163.         SetGlobal(paramPtr, tempStr, PasToZero(paramPtr, msgStr));
  164.                             { and fill it }
  165.         paramPtr^.returnValue := PasToZero(paramPtr, '');  { return empty }
  166.       end
  167.     else
  168.   { requested noDialog only so we return the error condition as the result }
  169.       paramPtr^.returnValue := PasToZero(paramPtr, msgStr);
  170.   end;  { procedure }
  171.  
  172.   function AskedForHelp (paramPtr: XCmdPtr;
  173.                   syntaxMsg: Str255;
  174.                   copyrightMsg: Str255): boolean;
  175. {  check to see if the user sent a '?' or a '!' as }
  176. { the only parameter. if so we will respond with }
  177. { the calling syntax or the copyright/version info }
  178. { for this external }
  179. {}
  180.     var
  181.       firstStr: str255;
  182.   begin
  183.     askedForHelp := false;
  184.     if paramPtr^.paramCount = 1 then
  185.       begin
  186.         ZeroToPas(paramPtr, paramPtr^.params[1]^, firstStr);
  187.           { what is the first param? }
  188.         if firstStr = '?' then
  189.           begin
  190.             reportToUser(paramPtr, syntaxMsg);
  191.             askedForHelp := true
  192.           end  { asked for help }
  193.         else if firstStr = '!' then
  194.           begin
  195.             reportToUser(paramPtr, copyRightMsg);
  196.             askedForHelp := true
  197.           end;  { asked for copyright info }
  198.       end;  { one parameter passed }
  199.   end;  { function }
  200.  
  201.   function PathNameFromDirID (dirID: longint;
  202.                   vRefnum: integer;
  203.                   var fullPathName: str255): OSErr;
  204. { build up a full path name given a directory id and an vol ref num.  this method isn't reccomended in general (see the }
  205. {  various tech notes), but we use it in HC externals as HC uses exclusively full path names }
  206.     var
  207.       myCPB: CInfoPBRec;
  208.       directoryName: str255;
  209.       err: OSErr;
  210.   begin
  211.     fullPathName := '';
  212.     with myCPB do
  213.       begin
  214.         ioNamePtr := @directoryName;
  215.         ioDrParID := DirId;
  216.       end;
  217.  
  218.     repeat
  219.       with myCPB do
  220.         begin
  221.           ioVRefNum := vRefNum;
  222.           ioFDirIndex := -1;
  223.           ioDrDirID := myCPB.ioDrParID;
  224.         end;
  225.       err := PBGetCatInfo(@myCPB, FALSE);
  226.  
  227.       directoryName := concat(directoryName, ':');
  228.  
  229. { pascal strings mustn't be longer than 255 chars, though a path name may, so check }
  230.       if length(directoryName) + length(fullPathName) <= 255 then
  231.         fullPathName := concat(directoryName, fullPathName)
  232.       else
  233.         myCPB.ioDrDirID := fsRtDirID;    { lazy persons way to jump out }
  234.  
  235.     until (myCPB.ioDrDirID = 2);
  236.     PathNameFromDirID := err;
  237.   end;
  238.  
  239.   function StrToRect (paramPtr: XCMDPtr;
  240.                   rectStr: Str255): Rect;
  241. { convert a string, as from a callback or a passed parameter, to a rect }
  242.     var
  243.       where: Integer;
  244.       tempRect: rect;
  245.   begin
  246.     where := POS(',', rectStr);
  247.     tempRect.left := StrToNum(paramPtr, COPY(rectStr, 1, where - 1));
  248.     DELETE(rectStr, 1, where);
  249.  
  250.     where := POS(',', rectStr);
  251.     tempRect.top := StrToNum(paramPtr, COPY(rectStr, 1, where - 1));
  252.     DELETE(rectStr, 1, where);
  253.  
  254.     where := POS(',', rectStr);
  255.     tempRect.right := StrToNum(paramPtr, COPY(rectStr, 1, where - 1));
  256.     DELETE(rectStr, 1, where);
  257.  
  258.     tempRect.bottom := StrToNum(ParamPtr, rectStr);
  259.  
  260.     strToRect := tempRect;
  261.   end;
  262.  
  263.   function HCWindowRect (paramPtr: XCMDPtr): rect;
  264. { the rect of HC's card window, in GLOBAL coordinates }
  265.     var
  266.       theResult: Handle;
  267.       rectStr: str255;
  268.       theLength: INTEGER;
  269.   begin
  270.     rectStr := 'the rect of card window';
  271.     theResult := EvalExpr(paramPtr, rectStr);
  272.     if (theResult <> nil) and (paramPtr^.result = noErr) then
  273.       ZeroToPas(paramPtr, theResult^, rectStr)
  274.     else
  275.       rectStr := '';
  276.     if (theResult <> nil) then
  277.       DisposHandle(theResult);
  278.     HCWindowRect := StrToRect(paramPtr, rectStr);
  279.   end;
  280.  
  281.   function GetScreenSize: rect;
  282.   { we don't have access to quick draw globals, as they lie in HC's global space, but we can }
  283.   { get the monitor size indirectly by checking the portBits field of the window manager port }
  284.   { MacRevealed vol 3, pg 20 }
  285.     var
  286.       deskPort: GrafPtr;
  287.       tempRect: rect;
  288.   begin
  289.     GetWMgrPort(deskPort);    { grab a pointer to the window manager port }
  290.     if deskPort = nil then
  291.       begin
  292.         setRect(tempRect, 0, 0, 512, 342);
  293.         GetScreenSize := tempRect;
  294.       end
  295.     else
  296.       GetScreenSize := deskPort^.portBits.bounds;
  297.   end;
  298.  
  299.   function monitorRect (aPoint: point): rect;
  300.   { given a point, return the rect of the monitor that contains it.}
  301.     const
  302.       SysEnvVersion = 2;
  303.     var
  304.       currGDevice: GDHandle;
  305.       gotTheMonitor: boolean;
  306.       tempRect: rect;
  307.       theSysEnv: SysEnvRec;
  308.       envErr: OSErr;
  309.   begin
  310.     currGDevice := nil;
  311.     envErr := SysEnvirons(SysEnvVersion, theSysEnv);
  312.   {SysEnvirons Version is a constant in the interface section of this file}
  313.     if theSysEnv.hasColorQD then    { only proceed if we have color QD }
  314.       begin
  315.         currGDevice := GetDeviceList;
  316.         gotTheMonitor := false;    { haven't found the monitor yet }
  317.         while (currGDevice <> nil) and not (gotTheMonitor) do
  318.     { we assume that the point is in one of the graphic devices }
  319.           begin
  320.             if PtInRect(aPoint, currGDevice^^.gdRect) then
  321.               begin
  322.                 monitorRect := currGDevice^^.gdRect;
  323.                 gotTheMonitor := true;
  324.               end
  325.             else    { get the next device in the list }
  326.               currGDevice := currGDevice^^.gdNextGD;
  327.           end;
  328.         if currGDevice = nil then
  329.           begin
  330.             setRect(tempRect, 0, 0, 0, 0);
  331.             monitorRect := tempRect;
  332.           end;
  333.       end
  334.     else  {No Color QD}
  335.       begin
  336.         tempRect := GetScreenSize;
  337.         if PtInRect(aPoint, tempRect) then
  338.           monitorRect := tempRect
  339.         else
  340.           begin
  341.             setRect(tempRect, 0, 0, 0, 0);
  342.             monitorRect := tempRect;
  343.           end;
  344.       end;
  345.   end;
  346.  
  347.   function CenterInHCWindow (paramPtr: XCMDPtr;
  348.                   windowRect: rect): point;
  349.     var
  350.       where: point;
  351.       window, screen, tempRect: rect;
  352.       h, v: integer;
  353.   begin
  354.     window := HCWindowRect(paramPtr);    { the rect of card the window }
  355.     screen := monitorRect(window.topLeft);
  356.   { check to see the rect of the monitor containing the upper right corner of the card window }
  357.     setRect(tempRect, 0, 0, 0, 0);
  358.     if EqualRect(screen, tempRect) then
  359.   { if '0,0,0,0' comes back then the upper right is off screen, check the upper left }
  360.       begin
  361.         setPt(where, window.right, window.top);
  362.         screen := monitorRect(where);
  363.       end;
  364.  
  365.     OffsetRect(windowRect, window.left - windowRect.left, window.top - windowRect.top);
  366.   { zero the dlog rect onto the card window }
  367.     h := ((window.right - window.left) - (windowRect.right - windowRect.left)) div 2;
  368.     v := ((window.bottom - window.top) - (windowRect.bottom - windowRect.top)) div 2;
  369.     OffSetRect(windowRect, h, v);
  370.  
  371.   { although it isn't possible to have BOTH upper corners off screen, check for an error. }
  372.   { if we find one, use the default monitor rect }
  373.     if EqualRect(screen, tempRect) then
  374.       screen := GetScreenSize;
  375.  
  376.   { now center the rect in the card window }
  377.     if not (PtInRect(windowRect.topLeft, screen) and PtInRect(windowRect.botRight, screen)) then
  378.       begin    { make sure the dlog rect is fully visible on the screen }
  379.         if windowRect.top < screen.top then
  380.           OffSetRect(windowRect, 0, screen.top - windowRect.top + 10);
  381.         if windowRect.bottom > screen.bottom then
  382.           OffSetRect(windowRect, 0, screen.bottom - windowRect.bottom - 10);
  383.         if windowRect.left < screen.left then
  384.           OffSetRect(windowRect, screen.left - windowRect.left + 10, 0);
  385.         if windowRect.right > screen.right then
  386.           OffSetRect(windowRect, screen.right - windowRect.right - 10, 0);
  387.       end;
  388.     SetPt(where, windowRect.left, windowRect.top);
  389.     CenterInHCWindow := where;
  390.   end;
  391.  
  392.   function unSignedByte (SB: signedByte): integer;
  393.     type
  394.       twoSBAreAnInt = record
  395.           case integer of
  396.             0: (
  397.                 sbArray: array[0..1] of SignedByte
  398.             );
  399.             1: (
  400.                 Int: integer
  401.             );
  402.         end;
  403.     var
  404.       tempInt: twoSBAreAnInt;
  405.   begin
  406.     tempInt.Int := 0;
  407.     tempInt.sbArray[1] := SB;
  408.     unSignedByte := tempInt.int;
  409.   end;
  410.  
  411.   function insertCommas (theNumber: str255): str255;
  412.   { Procedure to insert commas every 3 numeric digits}
  413.     var
  414.       count, group: integer;
  415.   begin
  416.     group := 0;
  417.     for count := length(theNumber) downto 1 do
  418.       begin
  419.         group := group + 1;
  420.         if (group <> 3) or (count = 1) then
  421.           cycle;
  422.         insert(',', theNumber, count);
  423.         group := 0;
  424.       end;
  425.     insertCommas := theNumber;
  426.   end;
  427.  
  428.   procedure drawFreeSpace (theDialog: DialogPtr);
  429.   { draw the amount of free space into the dialog, just above item #5, the eject button }
  430.     var
  431.       thePort: GrafPtr;
  432.       oldFont, oldSize: integer;
  433.       freeSpace: longint;
  434.       freeStr: str255;
  435.       PB: ParamBlockRec;
  436.       strWidth: integer;
  437.       volInfoErr: OSerr;
  438.       eraseArea: rect;
  439.       itemType, left: integer;
  440.       itemHndl: handle;
  441.       itemRect: rect;
  442.   begin
  443.     GetPort(thePort);
  444.     if thePort <> nil then
  445.       begin
  446.         PB.iovRefNum := -(integerPtr(kSFSaveDisk)^);     { grab the VRefNum directly from lo mem}
  447.         PB.ioVolIndex := 0;                         { use vRefNum only }
  448.         PB.ioNamePtr := @freeStr;                   { VERY IMPORTANT!  Tell PBGetVInfo where to }
  449.         volInfoErr := PBGetVInfo(@PB, false);           { put the vol name, even though we don't use it }
  450.  
  451.         if volInfoErr = noErr then
  452.           begin
  453.             FreeSpace := (PB.ioVAlBlkSiz * PB.ioVFrBlk) div 1024;       { Calc the free size}
  454.             NumToString(FreeSpace, FreeStr);
  455.             FreeStr := insertCommas(FreeStr);
  456.           end
  457.         else
  458.           begin
  459.             FreeStr := '????';         { If an error occured, show question marks}
  460.           end;
  461.  
  462.         FreeStr := concat(FreeStr, 'k free');
  463.         oldFont := thePort^.txFont;    { remember the old font }
  464.         oldSize := thePort^.txSize;    { and the size }
  465.         TextFont(3);            { set text to geneva }
  466.         TextSize(9);            { 9 point }
  467.         GetDItem(theDialog, 5, itemType, itemHndl, itemRect);         { Get the coordinates of the Eject button}
  468.         with itemRect do
  469.           setRect(eraseArea, itemRect.left - 5, itemRect.top - 11, itemRect.right + 5, itemRect.top);
  470.         eraseRect(eraseArea);
  471.         strWidth := StringWidth(FreeStr);
  472.         left := ((itemRect.right - itemRect.left) div 2) + itemRect.left;
  473.         MoveTo(left - (strWidth div 2), itemRect.top - 2);  { move the pen}
  474.         DrawString(FreeStr);      { show em how much free space they have... }
  475.         TextFont(oldFont);        { set font to the original }
  476.         TextSize(oldSize);        { and the size }
  477.       end;
  478.   end;
  479.  
  480.   function getVolDlgHook (item: Integer;
  481.                   theDialog: DialogPtr): Integer;
  482.     var
  483.       itemType: integer;
  484.       itemHndl: handle;
  485.       itemRect: rect;
  486.   begin
  487.     getVolDlgHook := item;
  488.     case item of
  489.       -1: 
  490.         begin
  491.     { Change the name of the open button to 'Select'}
  492.           GetDItem(theDialog, getOpen, itemType, itemHndl, itemRect);
  493.           SetCTitle(controlHandle(itemHndl), 'Select');
  494.         end;
  495.       103: 
  496.         getVolDlgHook := 1;  { Convert the open directory event to quit SFGetFile}
  497.     end;
  498.   end;
  499.  
  500.   function getVolFileFilter (PB: ParamBlockRec): boolean;
  501.   begin
  502.   { 'show' all files so that at least a desktop file will be shown offscreen and the 'Open' button is enabled}
  503.     getVolFileFilter := false;
  504.   end;
  505.  
  506.   function getStdDlgFilter (theDialog: DialogPtr;
  507.                   var theEvent: eventRecord;
  508.                   var itemHit: integer): boolean;
  509.   { A dialog filter is usually unneeded for simple std. file stuff.  We use one here so that we can draw the}
  510.   { freespace for the current volume.  The string is drawn not put as a static text item so that we can use a}
  511.   { different font for the string.  Because we draw on an update event we must compensate for std file's bug}
  512.   { which confuses update events meant for windows behind it.  Thus if we see an update event for someone }
  513.   { elses window change the event to a NULL and tell ModalDialog that we've handled it. }
  514.   begin
  515.     getStdDlgFilter := false;  { Pass Standard File package handle all events}
  516.     case theEvent.what of
  517.  
  518.       updateEvt: 
  519.         if DialogPtr(theEvent.message) <> theDialog then
  520.           begin
  521.             itemHit := 100;      { change the event to a NULL }
  522.             getStdDlgFilter := true;  { tell Standard File package that we have handled it}
  523.           end
  524.         else
  525.           drawFreeSpace(theDialog);  { update our free space indicator }
  526.  
  527.       otherwise       { a do nothing case}
  528.     end;  {case}
  529.   end;    {getFileDlgFilter}
  530.  
  531.   procedure SFGetVolume (pt: point;
  532.                   var reply: SFReply);
  533.   { this is the main routine.  It takes a different approach to customizing SFGetFile}
  534.     type
  535.       DITLItem = record              { First, a single item}
  536.           itmHndl: Handle;              { Handle or procPtr for this item}
  537.           itmRect: Rect;              { Display rectangle for this item}
  538.           itmType: integer;            { Item type for this item is in hi byte.  length of next field is in lo byte}
  539.         {  itmData: itmDataLength bytes      must be even}
  540.         end;    {DITLItem}
  541.       pDITLItem = ^DITLItem;
  542.       hDITLItem = ^pDITLItem;
  543.  
  544.       ItemList = record        { Then, the list of items}
  545.           dlgMaxIndex: Integer;   { Number of items minus 1}
  546.           DITLItems: array[0..0] of DITLItem;    { Array of items}
  547.         end;                  { ItemList}
  548.       pItemList = ^ItemList;
  549.       hItemList = ^pItemList;
  550.     var
  551.       typeList: SFTypeList;
  552.       dlogHndl: DialogTHndl;
  553.       ditlID: integer;
  554.       dlogSize: rect;
  555.       ditlHndl: hItemList;
  556.       ItemInDITL: pDITLItem;
  557.       savedApplScratch: longint;
  558.       oldPort: grafPtr;
  559.  
  560.     function EvenByte (value: integer): integer;
  561.   { returns the smallest even number equal to or greater than value}
  562.       var
  563.         tempInt: integer;
  564.     begin
  565.       tempInt := bitAND(value, $00FF);
  566.       EvenByte := tempInt + (tempInt mod 2);
  567.     end;
  568.  
  569.   begin
  570.   { We want a very small dialog with no file list item.  The problem is that if we move items around}
  571.   { with a regular DialogHook routine, the useritems (the file list & the volume name) are already}
  572.   { initialized and their procedures draw where the items ORIGINALLY were before we moved them!}
  573.   { Sooooo, we load the DITL resource and lock it in memory.  We then move everything around in}
  574.   { the DITL before we ever call the SFPGetFile toolbox call.}
  575.  
  576.   { First load the DLOG resource and find out which DITL we are supposed to load}
  577.     dlogHndl := DialogTHndl(GetResource('DLOG', getDlgID));
  578.     if dlogHndl = nil then
  579.       begin
  580.         reply.good := false;       {If the DLOG could not be loaded, fail out}
  581.         exit(SFGetVolume);
  582.       end;
  583.     MoveHHI(handle(dlogHndl));
  584.     HNoPurge(handle(dlogHndl));
  585.     HLock(handle(dlogHndl));
  586.     dlogsize := dlogHndl^^.boundsRect;
  587.   { size the window to 110 horizontally and 180 vertically}
  588.     setRect(dlogHndl^^.boundsRect, dlogsize.left, dlogsize.top, dlogsize.left + 109, dlogsize.top + 179);
  589.     ditlID := dlogHndl^^.itemsID;
  590.     HUnLock(handle(dlogHndl));
  591.  
  592.   { Now get the DITL}
  593.     ditlHndl := hItemList(GetResource('DITL', ditlID));
  594.     if ditlHndl = nil then
  595.       begin       { Since we could not load the resource, abort}
  596.         reply.good := false;
  597.         HPurge(handle(dlogHndl));
  598.         DetachResource(handle(dlogHndl));
  599.         DisposHandle(handle(dlogHndl));
  600.         exit(SFGetVolume);
  601.       end;
  602.     MoveHHI(handle(ditlHndl));
  603.     HNoPurge(handle(ditlHndl));
  604.     HLock(handle(ditlHndl));
  605.  
  606.   { Now move everybody around.  Use the routine at least once to get an idea of what we are doing here}
  607.   { Item 1 is the Open button}
  608.     ItemInDITL := pDITLItem(ORD4(ditlHndl^) + 2);
  609.     setRect(ItemInDITL^.itmRect, 15, 109, 95, 127);
  610.   { Item 2 is an invisible, unused button}
  611.     ItemInDITL := pDITLItem(ORD4(ItemInDITL) + 14 + EvenByte(ItemInDITL^.itmType));
  612.   { Item 3 is the Cancel button}
  613.     ItemInDITL := pDITLItem(ORD4(ItemInDITL) + 14 + EvenByte(ItemInDITL^.itmType));
  614.     setRect(ItemInDITL^.itmRect, 15, 134, 95, 152);
  615.   { Item 4 is the UserItem for DiskName}
  616.     ItemInDITL := pDITLItem(ORD4(ItemInDITL) + 14 + EvenByte(ItemInDITL^.itmType));
  617.     setRect(ItemInDITL^.itmRect, 1, 17, 112, 38);
  618.   { Item 5 is the Eject button}
  619.     ItemInDITL := pDITLItem(ORD4(ItemInDITL) + 14 + EvenByte(ItemInDITL^.itmType));
  620.     setRect(ItemInDITL^.itmRect, 15, 51, 95, 69);
  621.   { Item 6 is the Drive button}
  622.     ItemInDITL := pDITLItem(ORD4(ItemInDITL) + 14 + EvenByte(ItemInDITL^.itmType));
  623.     setRect(ItemInDITL^.itmRect, 15, 76, 95, 94);
  624.   { Item 7 is the userItem for the file name list}
  625.     ItemInDITL := pDITLItem(ORD4(ItemInDITL) + 14 + EvenByte(ItemInDITL^.itmType));
  626.     setRect(ItemInDITL^.itmRect, 112, 39, 330, 185);
  627.   { Item 8 is the user item for the scroll bar}
  628.     ItemInDITL := pDITLItem(ORD4(ItemInDITL) + 14 + EvenByte(ItemInDITL^.itmType));
  629.     setRect(ItemInDITL^.itmRect, 329, 39, 351, 185);
  630.   { Item 9 is the useritem for the dotted line}
  631.     ItemInDITL := pDITLItem(ORD4(ItemInDITL) + 14 + EvenByte(ItemInDITL^.itmType));
  632.     setRect(ItemInDITL^.itmRect, 10, 101, 100, 102);
  633.   { Item 10 is the invisible, unused statText}
  634.     ItemInDITL := pDITLItem(ORD4(ItemInDITL) + 14 + EvenByte(ItemInDITL^.itmType));
  635.  
  636.     HUnLock(handle(ditlhndl));
  637.  
  638.     GetPort(oldPort);  { Save anything that we might change}
  639.  
  640.   { NOW make the toolbox call}
  641.     SFPGetFile(pt, '', @getVolFileFilter, -1, TypeList, @getVolDlgHook, reply, getdlgID, @getStdDlgFilter);
  642.  
  643.     SetPort(oldPort);  { and restore anything that we might change}
  644.  
  645.   { Since this routine selects volumes, pass the root dir id back.  reply.vRefNum is already correct}
  646.     reply.fType := ResType(longint(fsRtDirID));
  647.  
  648.   { Now clean up the mess you've made!!!}
  649.   { (Otherwise the next call to SFGetFile will be REALLY interesting!)}
  650.     HPurge(handle(dlogHndl));
  651.     DetachResource(handle(dlogHndl));
  652.     DisposHandle(handle(dlogHndl));
  653.     HPurge(handle(ditlHndl));
  654.     DetachResource(handle(ditlHndl));
  655.     DisposHandle(handle(ditlHndl));
  656.   end;
  657.  
  658.   procedure VolumeName (paramPtr: XCMDPtr);
  659.     var
  660.       reply: SFReply;
  661.       pathName: str255;
  662.       prompt: str255;
  663.       thePt: point;
  664.       tempRect: rect;
  665.       err: OSErr;
  666.   begin
  667.   { First check to see if the user requested syntax or copyright information}
  668.   { If they did, we exit the XFCN.  The subroutine takes care of returning the proper string}
  669.     if askedForHelp(paramPtr, 'VolumePath()', 'v1.1, ┬⌐1989 Apple Computer, Inc. by Anup Murarka & Eric Carlson') then
  670.       exit(VolumeName);
  671.  
  672.   {110 by 180 are the dimensions of our custom dialog box}
  673.     SetRect(tempRect, 0, 0, 110, 180);
  674.     thePt := CenterInHCWindow(paramPtr, tempRect);    { This glue routine centers our dialog}
  675.  
  676.     SFGetVolume(thePt, reply);                      { This routine does the real work.  Found in CustomSF.p}
  677.     if reply.good then                            { If a volume was selected, return the pathname}
  678.       begin
  679.         err := PathNameFromDirID(longint(reply.fType), reply.vRefNum, pathName);
  680.         paramPtr^.returnValue := PasToZero(paramPtr, pathName);
  681.       end;
  682.   end;
  683. end.